﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace OperationPlugins.Proxies.CodeDom
{
    /// <summary>
    /// A collection of assemblies.
    /// </summary>
    public sealed class AssemblyCollection : List<Assembly>
    {
        /// <summary>
        /// No public constructor available.
        /// </summary>
        internal AssemblyCollection()
        {            
        }

        /// <summary>
        /// Adds an assembly to the collection if it doesn't already exists in the collection.
        /// </summary>
        /// <param name="assembly">The assembly to be added.</param>
        public void AddIfNotExists(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }

            if (Contains(assembly) == false)
            {
                Add(assembly);
            }
        }

        /// <summary>
        /// Gets assembly locations.
        /// </summary>
        /// <returns>Assembly locations.</returns>
        internal string[] GetLocations()
        {
            return this.Select(reference => reference.Location).ToArray();
        }

        internal void AddFromType(Type type)
        {
            Assembly assembly = type.Assembly;

            AddIfNotExists(assembly);

            foreach (AssemblyName an in assembly.GetReferencedAssemblies())
            {
                AddFromAssembly(an, 0);
            }

            foreach (Type gt in type.GenericTypeArguments)
            {
                AddFromType(gt);
            }
        }

        private void AddFromAssembly(AssemblyName assemblyName, int nestingLevel)
        {
            nestingLevel++;

            Assembly assembly = Assembly.Load(assemblyName);

            AddIfNotExists(assembly);

            if (nestingLevel < 5)
            {
                foreach (AssemblyName an in assembly.GetReferencedAssemblies())
                {
                    AddFromAssembly(an, nestingLevel);
                }
            }
        }

    }
}
